אבטחה, סיסמאות, הצפנה, גיבוב וקריפטוגרפיה זה בדרך כלל לא הנושאים שהכי מעניינים אותנו בתור מתכנתי PHP והרבה מאוד פעמים מהווים את הנקודה החלשה במערכות שלנו. רוצה להפסיק לשבור את הראש ולהישאר מאובטח?
עברה כמעט שנה מאז הפרסום הראשוני של המדריך הזה, אך אנשים ממשיכים לעשות את אותם הטעויות בהצפנת סיסמאות. ממשיכים להשתמש ב-md5, אולי עוברים ל-sha1, אחרים מוסיפים מלח (salt) לפונקציות הגיבוב שלהם, אבל שום דבר מזה לא מספיק. שני פונקציות חדשות שנוספו לשפת PHP ושימוש בלאגוריטם הצפנה מתעדכן מבטיח להשאיר אותך מאובטח.
גיבוב זו פעולה מאוד מהירה
פעולות כמו md5, sha1, sha2 עוצבו כדי לבצע בדיקה מהירה שהודעה שהתקבלה זהה להודעה שנלשחה ברשת וממש לא בשביל לייצר חאשים של סיסמאות. הם מהירות מדי בשביל סיסמאות.
אם בעבר היינו מוסיפים לסיסמה salt כלשהו כדי להגן מפני rainbow tables, היום כבר אין צורך ב rainbow table בגלל שלייצר אותו לוקח כלום זמן. המחשבים היום בעלי כוח עיבוד מטורף, אבל מעבר לזה הם בעלי כרטיסים גרפיים מתוחכמים עם המון ליבות וכוח חישובי מטורף.
כמות חישובי sha2 שיכול להוציא המעבד בשניה לאומת כרטיס גרפי
קופסה פשוטה ב 2000 דולר עם כמה כרטיסים גרפיים יכולה להוציא כמעט כל סיסמה בזמן סביר.
יש פונקציה איטית?
תאר לעצמכך שהיית פונקציה שהיה לוקח לה בדיוק שניה אחת לחשב חאש של מחרוזת. לא משנה כמה מעבדים יהיה לפורץ במקרה הזה, הוא יוכל לנסות לנחש סיסמה אחת בשניה מתוך מליוני האופציות שיש בפועל.
הכי קרוב לזה שיש לנו בPHP זה bcrypt שבתור פרמטר יודעת לקבל רמת קושי - ראונדס. הרעיון בראוונדס הגבוהים הוא לגרום לפעולה של ההאש לקחת הרבה יותר זמן על ידי חישוב חאש 1000 פעם במקום פעם אחת.
bcrypt קצת מסובך
יותר נכון השימוש בו ב-PHP קצת מסובך בגלל שצריך ליצור salt במבנה מאוד מסוים שיכלול את כל המידע על זה לגבי כמה מסובכת צריכה להיות ההצפנה. בגלל שלנו אין כוח להתעסק בזה - מפתחי ה PHP החליטו להוסיף לשפה 2 פונקציות חדשות שיעשו הכל בשבילנו.
password_hash($password, $algo)
password_hash היא פונקציה מובנית בPHP שמטרתה היא לגבב (ליצור hash) של סיסמאות.
הפונקציה הזו מקבלת שני פרמטרים:
— את הסיסמה שצריך להצפין (qwerty)
— את אלגוריטם הגיבוב שבה תשתמש (PASSWORD_BCRYPT)
היא תייצר את ה-salt בעצמה בצורה שרירותית וכתוצאה תקבל hash של סיסמה אותו אפשר להכניס למסד נתונים או להשתמש בו לצורך השוואת סיסמה בסופו של דבר.
$hashed = password_hash($pass, PASSWORD_BCRYPT);
שים לב שבתור אלגוריטם בחרנו את bcrypt, שנחשב לאלגוריטם מספק היום, אבל מה יקרה בעוד שנה, שנתיים כשהמחשבים ישתדרגו? לאף אחד מאיתנו אין בתוכניות להיות עם היד על הדופק כשמשהו משתנה ומיד לחזור אל הקוד עוד שנתיים כדי לשנות את אלגוריטם ההצפנה.
בשביל לא להתריח אותנו יותר מדי, מפתחי השפה הכניסו קבוע נוסף PASSWORD_DEFAULT שהולך להשתנות לבד מגירסה לגירסה ברגע שמשהו יתעדכן בעולם האבטחה. במקום לכתוב בפרמטר השני, בצורה מפורשת, באיזה אלגוריטם נרצה להשתמש - נוכל לכתוב PASSWORD_DEFAULT ו-PHP תבחר את האלגוריטם המתאים ביותר לאותה תקופה ולאות גרסת PHP.
$hashed = password_hash($pass, PASSWORD_DEFAULT);
if(password_verify($_POST['pw'], $hashed))
בדיקת סיסמה מתבצעת בשורה אחת על ידי פונקציה מובנית נוספת - password_verify
הפרמטר הראשון זו הסיסמה שהבן אדם כרגע הזין ואנחנו רוצים לבדוק שהיא נכונה
הפרמטר השני זה ה-hash ששמור במסד כתוצעה מ-password_hash
רגע, זה לא מPHP5.5 ?
למרות שהפונקציות האלה מופיעות רק ב PHP 5.5 - אתה יכול להתחיל להשתמש בהם כבר עכשיו.
פשוט תעשה אינקלוד לקובץ password_compat ותתחיל להשתמש. הקוד יבדוק לבד את גרסת ה-PHP שלך ובהתאם לזה יחליט אם להשתמש בפונקציות המובנות בשפה עצמה או בפונקציות הממומשות בקובץ.
דוגמה לעבודה עם סיסמאות במסד
הרשמה:
$passwordHash = password_hash($_POST['pass'], PASSWORD_DEFAULT );
query(" INSERT INTO users (mail, pass) VALUES ('$mail', '$passwordHash')");
query(" INSERT INTO users (mail, pass) VALUES ('$mail', '$passwordHash')");
הזדהות:
$hashInDb = query("SELECT pass FROM users WHERE mail = '$mail'");
if(password_verify($_POST['pass'], $hashInDb)
echo 'welcome';
else
echo 'wrong pw';
if(password_verify($_POST['pass'], $hashInDb)
echo 'welcome';
else
echo 'wrong pw';
גיבוב ובדיקה של סיסמה הופכות לעניין של שורה אחת עם רמת אבטחה גבוה. בדוגמה למעלה אין צורך לבצע כל מני שטויות כמו real_escape_sting או htmlSpecialChars על הסיסמה, היות שהיא תעבור גיבוב וה-hash שלה בוודאות יהיה מספיק תקין בשביל להתקבל על ידי המסד.
אותו דבר נכון גם לגבי אימייל. מספיק שתבדוק שהאימייל כתוב נכון וזה יהיה מספיק מאובטח בשביל להכניס אותו למסד בלי escape_string או htmlSpecialChars. אימייל תקין לא יכול להכיל תווים מוזרים וגרשיים. ניתן לבדוק האם אימייל כתוב בצורה תקינה באמצעות פונקציות מובנות בשפת PHP.
תגובות לכתבה:
ענק!
התחלתי לקרוא, אמרתי נחמד אבל אני יודע שזה מ-php 5.5 אז זה לא שמיש כרגע.
משמח לדעת שאפשר להשתמש בזה כבר עכשיו. :-)
אני אישית הוספתי את זה לפני כמה שעות לאחד הפרוייקטים שלי.
עובד פצצה :)
יהיה טוב לציין ש-password_hash מחזירה מחרוזת באורך 60 תווים. רק כדי שידעו איזה אורך לקבוע במסד.
אגב, תודה רבה על הערה עידן,
ואני עוד יוסיף שאת השדה יש לקבוע לא בתור varchar אלה בתור binary(60
מדוע לא char(60)?
תודה. :)
תודה רבה!.
אשמח לדעת גם למה binary ולא char :).
בגלל שהפלט של הפונקציה הזו הוא ערך בינארי שכל בייט בו לאו דווקא מייצג אות כלשהי שאפשר לקרוא.
אם השדה יהיה char המסד יצרטך לעשות המרה של אותם בייטים לאותיות. ואין שום סיבה לעשות את זה.